Passed
Push — development ( 62539b...739f83 )
by Vad
10:43 queued 14s
created

BicyclesService.updatePositionsParallel   A

Complexity

Conditions 2

Size

Total Lines 28
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.2559

Importance

Changes 0
Metric Value
cc 2
eloc 23
dl 0
loc 28
ccs 6
cts 10
cp 0.6
crap 2.2559
rs 9.328
c 0
b 0
f 0
1 13
import { Injectable } from '@nestjs/common';
2 13
import { InjectRepository } from '@nestjs/typeorm';
3 13
import { In, Repository } from 'typeorm';
0 ignored issues
show
introduced by
'In' is defined but never used.
Loading history...
4 13
import { Bicycle } from './entities/bicycle.entity';
5 13
import { NotFoundException } from '@nestjs/common';
0 ignored issues
show
introduced by
'@nestjs/common' import is duplicated.
Loading history...
6
import { UpdateBicycleDto } from './dto/update-bicycle.dto';
7
import { BicycleResponse } from './types/bicycle-response.interface';
8 13
import { getDistance } from 'src/utils/geo.utils';
9
import { CreateBicycleDto } from './dto/create-bicycle.dto';
10 13
import { City } from 'src/cities/entities/city.entity';
11 13
import { CityName } from 'src/cities/types/city.enum';
12
import { BicyclePositionDto } from './dto/batch-update.dto';
13
import { BicycleBatchResponse } from './types/BicycleBatchResponse';
14
15
@Injectable()
16 13
export class BicyclesService {
17
  // ai generated code, asked to update previous function which was very inefficient
18
  async updatePositionsParallel(updates: BicyclePositionDto[]): Promise<BicycleBatchResponse[]> {
19 2
    try {
20 3
      const latitudeCases = updates.map((u) => `WHEN id = '${u.id}' THEN ${u.latitude}`).join(' ');
21
22 2
      const longitudeCases = updates
23 3
        .map((u) => `WHEN id = '${u.id}' THEN ${u.longitude}`)
24
        .join(' ');
25
26 2
      await this.bicycleRepository
27
        .createQueryBuilder()
28
        .update()
29
        .set({
30
          latitude: () => `CASE ${latitudeCases} ELSE latitude END`,
31
          longitude: () => `CASE ${longitudeCases} ELSE longitude END`,
32
        })
33
        .whereInIds(updates.map((update) => update.id))
34
        .execute();
35
36
      return updates.map((update) => ({
37
        id: update.id,
38
        success: true,
39
      }));
40
    } catch (error) {
41 3
      return updates.map((update) => ({
42
        id: update.id,
43
        success: false,
44
        error: error.message,
45
      }));
46
    }
47
  }
48
49
  constructor(
50
    @InjectRepository(Bicycle)
51 7
    private readonly bicycleRepository: Repository<Bicycle>,
52
    @InjectRepository(City)
53 7
    private cityRepository: Repository<City>,
54
  ) {}
55
56
  async findAll(): Promise<Bicycle[]> {
57
    const bikes = await this.bicycleRepository.find({
58
      relations: {
59
        city: true,
60
      },
61
      select: {
62
        id: true,
63
        batteryLevel: true,
64
        latitude: true,
65
        longitude: true,
66
        status: true,
67
        createdAt: true,
68
        updatedAt: true,
69
        city: {
70
          name: true,
71
        },
72
      },
73
    });
74
75
    return bikes;
76
  }
77
78
  async setRented(bikeId: string): Promise<Bicycle> {
79 2
    const result = await this.bicycleRepository.update(
80
      { id: bikeId, status: 'Available' },
81
      { status: 'Rented' },
82
    );
83 2
    if (result.affected === 0) {
84 1
      throw new NotFoundException(
85
        "Bike couldn't be rented. Bike might not exist or it is not available.",
86
      );
87
    }
88 1
    return await this.findById(bikeId);
89
  }
90
91
  async createBike(createBicycleDto: CreateBicycleDto): Promise<Bicycle> {
92
    const bike = this.bicycleRepository.create({
93
      batteryLevel: createBicycleDto.batteryLevel ?? 100,
94
      latitude: createBicycleDto.latitude,
95
      longitude: createBicycleDto.longitude,
96
      status: createBicycleDto.status ?? 'Available',
97
    });
98
99 2
    const city = createBicycleDto.city ?? 'Göteborg';
100
    // Find the city by name
101
    const cityEntity = await this.cityRepository.findOne({
102
      where: { name: city as CityName },
103
    });
104
105 1
    if (cityEntity) {
106
      bike.city = cityEntity;
107
    }
108
109
    return await this.bicycleRepository.save(bike);
110
  }
111
112
  async createManyBikes(createBicycleDto: CreateBicycleDto[]): Promise<Bicycle[]> {
113
    const defaultCity = await this.cityRepository.findOne({
114
      where: { name: CityName.Göteborg },
115
    });
116
    const Karlshamn = await this.cityRepository.findOne({
117
      where: { name: CityName.Karlshamn },
118
    });
119
    const Jönköping = await this.cityRepository.findOne({
120
      where: { name: CityName.Jönköping },
121
    });
122
123
    const bikes = createBicycleDto.map((bike) => {
124
      return this.bicycleRepository.create({
125
        batteryLevel: bike.batteryLevel ?? 100,
126
        latitude: bike.latitude,
127
        longitude: bike.longitude,
128
        status: bike.status ?? 'Available',
129
        city:
130
          bike.city === 'Jönköping'
131
            ? Jönköping
132
            : bike.city === 'Karlshamn'
133
              ? Karlshamn
134
              : defaultCity,
135
      });
136
    });
137
138
    return await this.bicycleRepository.save(bikes);
139
  }
140
141
  async findById(id: string): Promise<Bicycle> {
142
    const bike = await this.bicycleRepository.findOne({
143
      where: { id },
144
      relations: {
145
        city: true,
146
      },
147
    });
148 1
    if (!bike) {
149
      throw new NotFoundException('Bike not found');
150
    }
151
    return bike;
152
  }
153
154
  async update(id: string, updateBicycleDto: UpdateBicycleDto): Promise<Bicycle> {
155
    const bike = await this.findById(id);
156
157
    return this.bicycleRepository.save({ ...bike, ...updateBicycleDto });
158
  }
159
160
  async findByCity(cityName: CityName): Promise<Bicycle[]> {
161
    const bikes = await this.bicycleRepository.find({
162
      where: {
163
        city: {
164
          name: cityName,
165
        },
166
      },
167
      relations: ['city'],
168
    });
169
170
    return bikes;
171
  }
172
  async findByLocation(lat: number, lon: number, radius: number): Promise<Bicycle[]> {
173
    const allBikes = await this.findAll();
174
    const filteredBikes = allBikes.filter((bike) => {
175
      return getDistance(bike.latitude, bike.longitude, lat, lon) <= radius;
176
    });
177
    return filteredBikes;
178
  }
179
  async findByCityAndLocation(
180
    city: any,
181
    lat: number,
182
    lon: number,
183
    radius: number,
184
  ): Promise<Bicycle[]> {
185 2
    const bikesInCity = await this.findByCity(city);
186 2
    const filteredBikes = bikesInCity.filter((bike) => {
187 1
      return getDistance(bike.latitude, bike.longitude, lat, lon) <= radius;
188
    });
189 2
    return filteredBikes;
190
  }
191
192
  private toBicycleResponse(bike: Bicycle): BicycleResponse {
193
    return {
194
      id: bike.id,
195
      batteryLevel: bike.batteryLevel,
196
      latitude: bike.latitude,
197
      longitude: bike.longitude,
198
      status: bike.status,
199
      city: bike.city?.name,
200
      createdAt: bike.createdAt,
201
      updatedAt: bike.updatedAt,
202
    };
203
  }
204
205
  toBicycleResponses(bikes: Bicycle[]): BicycleResponse[] {
206
    return bikes.map((bike) => this.toBicycleResponse(bike));
207
  }
208
}
209